package configuration;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.HibernateTemplate;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;

import common.PropertyNames;
import dynamic.DynamicDatabase;

@Configuration
@PropertySource("classpath:conf/hibernate.properties")
@ComponentScan(value = { "dao", "entity", "service", "schedule" })
public class DynamicDatabaseConfiguration {
	/**
	 * Common Configuration
	 */

	private static final Logger logger = LoggerFactory.getLogger(DynamicDatabaseConfiguration.class);

	@Autowired
	Environment env;

	/**
	 * 
	 */
	public DynamicDatabaseConfiguration() {
		logger.info("[Initialize Dynamic Database]");
	}

	@Primary
	@Bean(name = "localDatasource")
	@ConfigurationProperties(prefix = "local.jdbc")
	public DriverManagerDataSource localDatasource() {
		DriverManagerDataSource dataSource = new DriverManagerDataSource();
		logger.info("Local connect url:" + env.getProperty(PropertyNames.LOCAL_URL));
		dataSource.setDriverClassName(env.getProperty(PropertyNames.LOCAL_DRIVER));
		dataSource.setUrl(env.getProperty(PropertyNames.LOCAL_URL));
		dataSource.setUsername(env.getProperty(PropertyNames.LOCAL_USER));
		dataSource.setPassword(env.getProperty(PropertyNames.LOCAL_PWD));
		return dataSource;
	}

	@Bean(name = "remoteDatasource")
	@ConfigurationProperties(prefix = "remote.jdbc")
	public DriverManagerDataSource remoteDatasource() {
		DriverManagerDataSource dataSource = new DriverManagerDataSource();
		logger.info("Remote connect url:" + env.getProperty(PropertyNames.REMOTE_URL));
		dataSource.setDriverClassName(env.getProperty(PropertyNames.REMOTE_DRIVER));
		dataSource.setUrl(env.getProperty(PropertyNames.REMOTE_URL));
		dataSource.setUsername(env.getProperty(PropertyNames.REMOTE_USER));
		dataSource.setPassword(env.getProperty(PropertyNames.REMOTE_PWD));
		return dataSource;
	}

	@Bean(name = "dynamicDatabase")
	public DynamicDatabase dynamicDatabase() {
		DynamicDatabase dynamicDatabase = new DynamicDatabase();
		Map<Object, Object> targetTransactionManagers = new HashMap<Object, Object>();
		targetTransactionManagers.put("localTransactionManager", localDatasource());
		targetTransactionManagers.put("remoteTransactionManager", remoteDatasource());
		dynamicDatabase.setTargetDataSources(targetTransactionManagers);
		dynamicDatabase.setDefaultTargetDataSource(remoteDatasource());
		return dynamicDatabase;
	}

	@Bean(name = "sessionFactory")
	public LocalSessionFactoryBean sessionFactory() {
		LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
		sessionFactory.setDataSource(dynamicDatabase());
		sessionFactory.setPackagesToScan(new String[] { "entity" });
		sessionFactory.setHibernateProperties(hibernateProperties());
		return sessionFactory;
	}

	@Bean(name = "hibernateTemplate")
	public HibernateTemplate hibernateTemplate() {
		HibernateTemplate template = new HibernateTemplate(sessionFactory().getObject());
		return template;
	}

	@Bean(name = "transactionManager")
	public HibernateTransactionManager transactionManager() {
		HibernateTransactionManager txManager = new HibernateTransactionManager();
		txManager.setSessionFactory(sessionFactory().getObject());
		txManager.setDataSource(dynamicDatabase());
		return txManager;
	}

	@SuppressWarnings("serial")
	public Properties hibernateProperties() {
		return new Properties() {
			{
				setProperty("hibernate.hbm2ddl.auto", env.getProperty(PropertyNames.LOCAL_HBM2DLL_ENABLED));
				setProperty("hibernate.show_sql", env.getProperty(PropertyNames.LOCAL_JDBC_SHOW_SQL));
				setProperty("hibernate.dialect", env.getProperty(PropertyNames.LOCAL_JDBC_DIALECT));
				// setProperty("hibernate.connection.username",
				// env.getProperty(PropertyNames.LOCAL_USER));
				// setProperty("hibernate.connection.password",
				// env.getProperty(PropertyNames.LOCAL_PWD));

				setProperty("hibernate.cache.region.factory_class",
						env.getProperty(PropertyNames.HIBERNATE_CACHE_FACTORY_CLASS));
				setProperty("hibernate.cache.use_second_level_cache",
						env.getProperty(PropertyNames.HIBERNATE_USE_SECOND_LEVEL_CACHE));
				setProperty("hibernate.cache.use_query_cache",
						env.getProperty(PropertyNames.HIBERNATE_USE_QUERY_CACHE));
				setProperty("org.hibernate.cache.ehcache.configurationResourceName",
						env.getProperty(PropertyNames.HIBERNATE_CACHE_RESOURCE_NAME));

				setProperty("hibernate.transaction.factory_class",
						env.getProperty(PropertyNames.HIBERNATE_TRANSACTION_FACTORY));
				setProperty("hibernate.jdbc.batch_size", env.getProperty(PropertyNames.HIBERNATE_BATCHSIZE));

				setProperty("hibernate.c3p0.minPoolSize", env.getProperty(PropertyNames.HIBERNATE_MIN_POOLSIZE));
				setProperty("hibernate.c3p0.maxPoolSize", env.getProperty(PropertyNames.HIBERNATE_MAX_POOLSIZE));
				setProperty("hibernate.c3p0.timeout", env.getProperty(PropertyNames.HIBERNATE_TIMEOUT));
				setProperty("hibernate.c3p0.max_statement", env.getProperty(PropertyNames.HIBERNATE_MAX_STATEMENT));
				setProperty("hibernate.c3p0.testConnectionOnCheckout",
						env.getProperty(PropertyNames.HIBERNATE_TEST_CONNECTION_ON_CHECKOUT));
			}
		};
	}

	@Bean
	public EhCacheCacheManager ehCacheCacheManager() {
		return new EhCacheCacheManager(ehCacheManagerFactoryBean().getObject());
	}

	@Bean
	public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
		EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();
		cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("conf/ehcache.xml"));
		cacheManagerFactoryBean.setShared(true);
		return cacheManagerFactoryBean;
	}

}
